#include "VoxelTerrain.h"
#include "Noise.h"

VoxelTerrain::VoxelTerrain(ID3D11Device *pDevice, ID3D11DeviceContext *pImmediateContext)
{
	pChunks = NULL;
	m_pVoxelChunkManager = NULL;
	
	m_pDevice = pDevice;
	m_pImmediateContext = pImmediateContext;
}
void VoxelTerrain::Release(void)
{
	for(int i = 0; i < pChunks->size(); i++ )
	{
		
		pChunks->at(i)->Release();
		delete pChunks->at(i);
	}
	pChunks->clear();
	delete pChunks;

	m_pVoxelChunkManager->Release();
	delete m_pVoxelChunkManager;

}

void VoxelTerrain::WriteVoxel(int x, int y, int z, Voxel * pVoxel)
{
	int chunkX = floorf((float)x / (float)D3DVoxelConstants::NonOverlappedVoxelCount);
	int chunkY = floorf((float)y / (float)D3DVoxelConstants::NonOverlappedVoxelCount);
	int chunkZ = floorf((float)z / (float)D3DVoxelConstants::NonOverlappedVoxelCount);
	
	int localX = x % D3DVoxelConstants::NonOverlappedVoxelCount;
	int localY = y % D3DVoxelConstants::NonOverlappedVoxelCount;
	int localZ = z % D3DVoxelConstants::NonOverlappedVoxelCount;
	int chunkIdx;

	if(localX < D3DVoxelConstants::PreviousChunkTail && chunkX > 0)
	{
		chunkIdx = D3DVoxelConstants::CalcChunkIndex(chunkX - 1, chunkY, chunkZ);
		Voxel* voxel = pChunks->at(chunkIdx)->GetVoxel(-1 + D3DVoxelConstants::NonOverlappedVoxelCount + (D3DVoxelConstants::PreviousChunkTail - localX) , localY, localZ);
		*voxel = *pVoxel;
	}
	else if(localX >= D3DVoxelConstants::NextChunkHead && chunkX < 2)
	{
		chunkIdx = D3DVoxelConstants::CalcChunkIndex(chunkX + 1, chunkY, chunkZ);
		Voxel* voxel = pChunks->at(chunkIdx)->GetVoxel(localX - D3DVoxelConstants::NextChunkHead , localY, localZ);
		*voxel = *pVoxel;
	}

	if(localY < D3DVoxelConstants::PreviousChunkTail && chunkY > 0)
	{
		chunkIdx = D3DVoxelConstants::CalcChunkIndex(chunkX, chunkY - 1, chunkZ);
		Voxel* voxel = pChunks->at(chunkIdx)->GetVoxel(localX , -1 + D3DVoxelConstants::NonOverlappedVoxelCount + (D3DVoxelConstants::PreviousChunkTail - localY), localZ);
		*voxel = *pVoxel;
	}
	else if(localY >= D3DVoxelConstants::NextChunkHead && chunkY < 2)
	{
		chunkIdx = D3DVoxelConstants::CalcChunkIndex(chunkX, chunkY + 1, chunkZ);
		Voxel* voxel = pChunks->at(chunkIdx)->GetVoxel(localX , localY - D3DVoxelConstants::NextChunkHead, localZ);
		*voxel = *pVoxel;
	}

	if(localZ < D3DVoxelConstants::PreviousChunkTail && chunkZ > 0)
	{
		chunkIdx = D3DVoxelConstants::CalcChunkIndex(chunkX, chunkY, chunkZ - 1);
		Voxel* voxel = pChunks->at(chunkIdx)->GetVoxel(localX , localY, -1 + D3DVoxelConstants::NonOverlappedVoxelCount + (D3DVoxelConstants::PreviousChunkTail - localZ));
		*voxel = *pVoxel;
	}
	else if(localZ >= D3DVoxelConstants::NextChunkHead && chunkZ < 2)
	{
		chunkIdx = D3DVoxelConstants::CalcChunkIndex(chunkX, chunkY, chunkZ + 1);
		Voxel* voxel = pChunks->at(chunkIdx)->GetVoxel(localX , localY, localZ - D3DVoxelConstants::NextChunkHead);
		*voxel = *pVoxel;
	}

	// two's
	/*if(localX < D3DVoxelConstants::PreviousChunkTail && chunkX > 0 && localY < D3DVoxelConstants::PreviousChunkTail && chunkY > 0)
	{
		chunkIdx = D3DVoxelConstants::CalcChunkIndex(chunkX - 1, chunkY - 1, chunkZ);
		Voxel* voxel = pChunks->at(chunkIdx)->GetVoxel(-1 + D3DVoxelConstants::NonOverlappedVoxelCount + (D3DVoxelConstants::PreviousChunkTail - localX) , -1 + D3DVoxelConstants::NonOverlappedVoxelCount + (D3DVoxelConstants::PreviousChunkTail - localY), localZ);
		*voxel = *pVoxel;
	}
	else if(localX >= D3DVoxelConstants::NextChunkHead && chunkX < 2 && localY >= D3DVoxelConstants::NextChunkHead && chunkY < 2)
	{
		chunkIdx = D3DVoxelConstants::CalcChunkIndex(chunkX + 1, chunkY + 1, chunkZ);
		Voxel* voxel = pChunks->at(chunkIdx)->GetVoxel(localX - D3DVoxelConstants::NextChunkHead  , localY - D3DVoxelConstants::NextChunkHead, localZ);
		*voxel = *pVoxel;
	}*/

	chunkIdx = D3DVoxelConstants::CalcChunkIndex(chunkX, chunkY, chunkZ);

	Voxel* voxel = pChunks->at(chunkIdx)->GetVoxel(localX, localY, localZ);
	*voxel = *pVoxel;
}
void VoxelTerrain::Init(void)
{
	
	m_pVoxelChunkManager = new VOXEL_TECHNIQUE(m_pDevice, m_pImmediateContext);
	if(m_pVoxelChunkManager->InitMCShaders() != S_OK)
	{
		int z = 0;
	}

	if(m_pVoxelChunkManager->InitRegularShaders() != S_OK)
	{
		int z = 0;
	}
	if(m_pVoxelChunkManager->InitSharedBuffers() != S_OK)
	{
		int z = 0;
	}
	

	pChunks = new vector<D3DVoxelChunk *>();

	Noise noise;
	noise.Initialize();

	for(int z = 0; z < CHUNK_AXIS_COUNT; z++ )
	{
		for(int y = 0; y < CHUNK_AXIS_COUNT; y++ )
		{
			for(int x = 0; x < CHUNK_AXIS_COUNT; x++ )
			{
				D3DVoxelChunk * chunk;
				chunk = new D3DVoxelChunk(m_pDevice, m_pImmediateContext);
				
				chunk->m_transform.x = x * D3DVoxelConstants::NonOverlappedVoxelCount;
				chunk->m_transform.y = y * D3DVoxelConstants::NonOverlappedVoxelCount;
				chunk->m_transform.z = z * D3DVoxelConstants::NonOverlappedVoxelCount;
				chunk->InitBuffers();
				chunk->InitStage3Buffers();
				pChunks->push_back( chunk );
			}
		}
	}
	Voxel vox;
	vox.cVolume = 255;

	vox.cType = 1;
	vox.cSubType = 1;
	vox.cOwner = 1;
	WriteVoxel(0, 1, 0, &vox);

	//WriteVoxel(3, 2, 1, &vox);
	D3DVoxelChunk * chunk;
	chunk = pChunks->at(0);
	//*chunk->GetVoxel(6,6,32) = vox;
	//*chunk->GetVoxel(6,6,32) = vox;
	//*chunk->GetVoxel(6,6,30) = vox;
	/**chunk->GetVoxel(7,6,32) = vox;
	*chunk->GetVoxel(7,6,31) = vox;*/
	//*chunk->GetVoxel(6,6,30) = vox;

	//chunk = pChunks->at(0 + 0 * CHUNK_AXIS_COUNT + 1 *  CHUNK_AXIS_COUNT_SQUARED);
	//*chunk->GetVoxel(6,6,0) = vox;
	if(false){
		for(int x = 0; x < CHUNK_AXIS_COUNT * D3DVoxelConstants::NonOverlappedVoxelCount; x++ )
		{
			for(int y = 0; y < CHUNK_AXIS_COUNT * D3DVoxelConstants::NonOverlappedVoxelCount; y++ )
			{
				for(int z = 0; z < CHUNK_AXIS_COUNT * D3DVoxelConstants::NonOverlappedVoxelCount; z++ )
				{
					Voxel vox;
					//vox.cVolume = (byte)(128.0f * (noise.GenNoise((float)x / 25, (float)y/ 25, (float)z/ 25) ));
					if(/*(x - 34.0f) * (x - 34.0f) + (y - 34.0f) * (y - 34.0f)
						+ (z - 34.0f) * (z - 34.0f) < 256.0f &&*/ x <= 5 && y <= 5 && z <= 5 && x > 3 && y > 3 && z > 3)
					{
						vox.cVolume = 255;
						vox.cType = 1;
						vox.cSubType = 1;
						vox.cOwner = 1;

						WriteVoxel(x, y, z, &vox);
					}
					else
						vox.cVolume = 1;

					
				}
			}
		}
	}

	//
	
	for(int x = 0; x < CHUNK_AXIS_COUNT; x++ )
	{
		for(int y = 0; y < CHUNK_AXIS_COUNT; y++ )
		{
			for(int z = 0; z < CHUNK_AXIS_COUNT; z++ )
			{
				D3DVoxelChunk * chunk;
				chunk = pChunks->at(D3DVoxelConstants::CalcChunkIndex(x, y, z));

				chunk->UpdateVoxelTexture();	
			}
		}
	}
}
void VoxelTerrain::Render(ID3D11Buffer *pViewMatrixBuffer, ID3D11Buffer *pProjectionMatrixBuffer)
{

	 //
    // Render the cube
    //
	static int cc = 0;
	static int inc = 1;
	
	if(false)
	{
		if(cc % 255 == 0)
		{
			inc *= -1;
		}
		for(int x = 0; x < CHUNK_AXIS_COUNT; x++ )
		{
			for(int y = 0; y < CHUNK_AXIS_COUNT; y++ )
			{
				for(int z = 0; z < CHUNK_AXIS_COUNT; z++ )
				{
					D3DVoxelChunk  * m_pVoxelChunk = pChunks->at(D3DVoxelConstants::CalcChunkIndex( x, y, z));
					
					float v = m_pVoxelChunk->GetVoxel(3,2,1)->cVolume;
					m_pVoxelChunk->GetVoxel(3,2,1)->cVolume += inc;

					m_pVoxelChunk->m_bMarkedDirty = true;
					m_pVoxelChunk->UpdateVoxelTexture();
				}
			}
		}
		

	}
	cc++;
	for(int x = 0; x < CHUNK_AXIS_COUNT; x++ )
	{
		for(int y = 0; y < CHUNK_AXIS_COUNT; y++ )
		{
			for(int z = 0; z < CHUNK_AXIS_COUNT; z++ )
			{
				D3DVoxelChunk * chunk;
				chunk = pChunks->at(D3DVoxelConstants::CalcChunkIndex( x, y, z));
				//m_pVoxelChunkManager->Render( chunk, pViewMatrixBuffer, pProjectionMatrixBuffer, chunk->m_pWorldMatrixBuffer );

				if(chunk->m_bMarkedDirty)
				{
					m_pVoxelChunkManager->ComputeIsosurface( chunk );
					HRESULT hr;
					//ID3D11InfoQueue * pInfoQueue = NULL; 
			  //      hr = g_pd3dDevice->QueryInterface(__uuidof(ID3D11InfoQueue), (LPVOID*)&g_pInfoQueue);

					//// Get the size of the message
			  //      SIZE_T messageLength = 0;
			  //      hr = g_pInfoQueue->GetMessage(0, NULL, &messageLength);
			  //      // Allocate space and get the message
			  //      D3D11_MESSAGE Message;
					//Message.pDescription = (char *)malloc(messageLength);
			  //      hr = g_pInfoQueue->GetMessage(0, &Message, &messageLength);
					//OutputDebugStringA(Message.pDescription);
				}

			}
		}
	}

	
	for(int x = 0; x < CHUNK_AXIS_COUNT; x++ )
	{
		for(int y = 0; y < CHUNK_AXIS_COUNT; y++ )
		{
			for(int z = 0; z < CHUNK_AXIS_COUNT; z++ )
			{
				D3DVoxelChunk * chunk;
				chunk = pChunks->at(D3DVoxelConstants::CalcChunkIndex( x, y, z));
				m_pVoxelChunkManager->Render( chunk, pViewMatrixBuffer, pProjectionMatrixBuffer, chunk->m_pWorldMatrixBuffer );

			}
		}
	}
}



float VoxelTerrain::SimplexFractalTerraced2(D3DXVECTOR3 Pos)
{
	D3DXVECTOR3 op = Pos;
	float ret;
	float func = sin(Pos.y / 100.0f);
	ret = abs(Pos.x - func)*1 / (sin(Pos.z)) ;


	return ret;
}
